package se.kth.ict.id2203.assignment3a;

import java.util.Set;

import org.apache.log4j.PropertyConfigurator;

import se.kth.ict.id2203.beb.BEB;
import se.kth.ict.id2203.beb.BEBInit;
import se.kth.ict.id2203.beb.BestEffortBroadcast;
import se.kth.ict.id2203.console.Console;
import se.kth.ict.id2203.console.java.JavaConsole;
import se.kth.ict.id2203.pfd.PFD;
import se.kth.ict.id2203.pfd.PFDInit;
import se.kth.ict.id2203.pfd.PerfectFailureDetector;
import se.kth.ict.id2203.pp2p.PerfectPointToPointLink;
import se.kth.ict.id2203.pp2p.delay.DelayLink;
import se.kth.ict.id2203.pp2p.delay.DelayLinkInit;
import se.kth.ict.id2203.riwc.AtomicRegister;
import se.kth.ict.id2203.riwc.RIWC;
import se.kth.ict.id2203.riwc.RIWCInit;
import se.sics.kompics.Component;
import se.sics.kompics.ComponentDefinition;
import se.sics.kompics.Fault;
import se.sics.kompics.Handler;
import se.sics.kompics.Kompics;
import se.sics.kompics.address.Address;
import se.sics.kompics.launch.Topology;
import se.sics.kompics.network.Network;
import se.sics.kompics.network.mina.MinaNetwork;
import se.sics.kompics.network.mina.MinaNetworkInit;
import se.sics.kompics.timer.Timer;
import se.sics.kompics.timer.java.JavaTimer;


public class Assignment3aMain extends ComponentDefinition {
	
	static {
		PropertyConfigurator.configureAndWatch("log4j.properties");
	}
	
	private static int selfId;
	private static String commandScript;
	
	Topology topology = Topology.load(System.getProperty("topology"), selfId);


	public static void main(String[] args) {
		
		selfId = Integer.parseInt(args[0]);
		commandScript = args[1];

		Kompics.createAndStart(Assignment3aMain.class);
	}

	/**
	 * Instantiates a new assignment1 group0.
	 */
	public Assignment3aMain() {
		// create components
		Component time = create(JavaTimer.class);
		Component network = create(MinaNetwork.class);
		Component con = create(JavaConsole.class);
		Component pp2p = create(DelayLink.class);
		Component app = create(Application3a.class);
		Component pfd = create(PFD.class);
		Component beb = create(BEB.class);
		Component riwc = create(RIWC.class);

		// handle possible faults in the components
		subscribe(handleFault, time.control());
		subscribe(handleFault, network.control());
		subscribe(handleFault, con.control());
		subscribe(handleFault, pp2p.control());
		subscribe(handleFault, app.control());
		subscribe(handleFault, pfd.control());
		subscribe(handleFault, beb.control());
		subscribe(handleFault, riwc.control());
		
		// initialize the components
		Address self = topology.getSelfAddress();
		Set<Address> neighborSet = topology.getNeighbors(self);

		trigger(new MinaNetworkInit(self, 5), network.control());
		trigger(new DelayLinkInit(topology), pp2p.control());
		trigger(new Application3aInit(commandScript, neighborSet, self), app
				.control());
		trigger(new PFDInit(topology, 20000, 20000), pfd.control());
		trigger(new BEBInit(topology), beb.control());
		trigger(new RIWCInit(topology), riwc.control());

		// connect the components
		connect(app.required(Console.class),
				con.provided(Console.class));
		
		connect(app.required(AtomicRegister.class),
				riwc.provided(AtomicRegister.class));
		
		connect(app.required(Timer.class), 
				time.provided(Timer.class));

		connect(riwc.required(BestEffortBroadcast.class),
				beb.provided(BestEffortBroadcast.class));
		
		connect(riwc.required(PerfectPointToPointLink.class),
				pp2p.provided(PerfectPointToPointLink.class));
		
		connect(riwc.required(PerfectFailureDetector.class),
				pfd.provided(PerfectFailureDetector.class));
		
		connect(beb.required(PerfectPointToPointLink.class),
				pp2p.provided(PerfectPointToPointLink.class));
		
		connect(pfd.required(Timer.class),
				time.provided(Timer.class));
				
		connect(pfd.required(PerfectPointToPointLink.class),
				pp2p.provided(PerfectPointToPointLink.class));
		
		connect(pp2p.required(Timer.class), time.provided(Timer.class));
		connect(pp2p.required(Network.class), network.provided(Network.class));

	}

	Handler<Fault> handleFault = new Handler<Fault>() {
		public void handle(Fault fault) {
			fault.getFault().printStackTrace(System.err);
		}
	};
}
